
package com.etu.upload;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import java.nio.ByteBuffer;

import java.nio.channels.*;



/**
 * 
 * @author Administrator
 *
 */
public abstract class UploadBase {


    // ----------------------------------------------------- Manifest constants


    /**
     * Thrown to indicate that the input stream fails to follow the
     * required syntax.
     */
    public static class MalformedStreamException
    extends IOException {
        /**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		/**
         * Constructs a <code>MalformedStreamException</code> with no
         * detail message.
         */
        public MalformedStreamException() {
            super();
        }

        /**
         * Constructs an <code>MalformedStreamException</code> with
         * the specified detail message.
         *
         * @param message The detail message.
         */
        public MalformedStreamException(String message) {
            super(message);
        }
    }


    /**
     * The Carriage Return ASCII character value.
     */
	private static final byte CR = 0x0D;//回车
	
	/**
     * The dash (-) ASCII character value.
     */
    private static final byte DASH = 0x2D;


    /**
     * The default length of the buffer used for processing a request.
     */
    protected static final int DEFAULT_BUFSIZE = 5120;


    /**
     * The maximum length of <code>header-part</code> that will be
     * processed (10 kilobytes = 10240 bytes.).
     */
    private static final int HEADER_PART_SIZE_MAX = 1024;


    /**
     * The Line Feed ASCII character value.
     */
    private static final byte LF = 0x0A;//换行


    /**
     * A byte sequence that marks the end of <code>header-part</code>
     * (<code>CRLFCRLF</code>).
     */
    private static final byte[] HEADER_SEPARATOR = {
        CR, LF, CR, LF };

    /**
     * 分隔符的预先符号换行--
     * */
    private static final byte[] PRE_SEPARATOR={CR,LF,DASH,DASH};
  

    /**
	 * 绑定的随机序列数
	 */
	private byte[] boundary;


	/**
	 * The length of the boundary token plus the leading <code>CRLF--</code>.
	 */
	protected int boundaryLength;


	/**
     * buffer数组的长度
     */
	protected int bufSize;


    /**
     * 这个数组用来储存来自inputStream 的数据.
     */
	protected final ByteBuffer buffer;




	/**
	 * The content encoding to use when reading headers.
	 */
	private String headerEncoding="utf-8";

	protected final ReadableByteChannel input;


	private int readednum=0;

    protected Header header;
	
	
	/**
	* 读取头部，一直读到两次换行
	* 
	* */
	private String readHeader(){
	    int i = 0;
	    byte b;
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    int size = 0;
	    String header=null;
	    try{
		    while (i < HEADER_SEPARATOR.length) {
		
		            b = readByte();
		
		        if (++size > HEADER_PART_SIZE_MAX) {
		            throw new MalformedStreamException(
		                    "Header section has more than " + 
		                    HEADER_PART_SIZE_MAX
		                    + " bytes (maybe it is not properly terminated)");
		        }
		        if (b == HEADER_SEPARATOR[i]) {
		            i++;
		        } else {
		            i = 0;
		        }
		        baos.write(b);
		    }
	    

        	header=baos.toString(headerEncoding);
       
	    baos.close();
	    } catch (UnsupportedEncodingException e) {
            // Fall back to platform default if specified encoding is not
            // supported.
        	header=baos.toString();
        }catch(IOException e){}
	    return header ;
	}


	/**
	 * 一次性读满缓冲区，然后冲缓冲区中读取byte数据
	 * @return byte
	 * */
	private byte readByte() throws IOException {
		
		if(buffer.position()==buffer.limit()){
		//tail=input.read(buffer,head,bufSize);    	
		readednum=input.read(buffer);
		buffer.flip();
		if(readednum==-1){
			throw new IOException("没有更多的数据可以读取了");
			}
		}
		return buffer.get();
	}

	/**
	 * 读取头部
	 * */
	private boolean readHeaders() {
    	while(true){
			String strHead=readHeader();    	
	    	header.parse(strHead);
	    	if(header.getFileName()!=null){
	    		return true;
	    	}else{
	    		header.setValue(readData());
	    		try {
					byte b=readByte();
					byte c=readByte();
					if(b==DASH&&c==DASH){
						return false;
					}
				} catch (IOException e) {
					
					e.printStackTrace();
					return false;
				}
	    	}
    	}
	}

	/**
	 * 读取普通表单数据，读到boundary就结束
	 * */
	private String readData(){
		 ByteArrayOutputStream baos = new ByteArrayOutputStream();
		 int i=0;
		 byte b;
		 String str=null;
		 try{
			 while(i<PRE_SEPARATOR.length){
			 b=readByte();
			 if(b==PRE_SEPARATOR[i]){
				 i++;				
			 }else{
				 if(i>0){					 
					 baos.write(PRE_SEPARATOR,0,i);
					 i=0;
				 }
				 baos.write(b);
			 	}
			 }
			 if(!readBoundary()){				
				 throw new MalformedStreamException("读取分隔符出错");
			 }
			 str=baos.toString(headerEncoding);
		 }catch(UnsupportedEncodingException e){
			 str=baos.toString();
		 }catch(IOException e){
			 
		 }
			 
		 return str;
	}



	private boolean readBoundary() throws IOException {
		int i=0;
		byte b;
		while(i<boundary.length){
			b=readByte();
			if(b==boundary[i]){
				i++;
			}else{
				return false;
			}
		}
		return true;
	}



	public UploadBase(RequestContextImpl req) throws IOException{
	      this(req,DEFAULT_BUFSIZE);
	}
	public UploadBase(RequestContextImpl req,int bufSize) throws IOException{
		this.input =Channels.newChannel(req.getInputStream());
	    this.bufSize =bufSize;// DEFAULT_BUFSIZE;
	    this.buffer = ByteBuffer.allocateDirect(bufSize);
	    byte[] boundary=req.getBoundary();
	    this.boundary = new byte[boundary.length];
	    this.boundaryLength = boundary.length+PRE_SEPARATOR.length;
	    
	
	    System.arraycopy(boundary, 0, this.boundary, 0, boundary.length);
	    
	    buffer.clear();
	    readednum=input.read(buffer);
	    buffer.flip();
	    
	    header=new Header();
		
		readHeaders();
	}


    /**
     * Specifies the character encoding to be used when reading the headers of
     * individual parts. When not specified, or <code>null</code>, the platform
     * default encoding is used.
     *
     * @param encoding The encoding used to read part headers.
     */
    public void setHeaderEncoding(String encoding) {
        headerEncoding = encoding;
    }
    




	public void setBufSize(int bufSize) {
		if(bufSize<0){
			return;
		}
		
		this.bufSize = bufSize;
	}

}
